package de.h_da.fbi.cg.pointcloud.adt;

import java.util.ArrayList;

import de.h_da.fbi.cg.pointcloud.spatial.IPoint;

/**
 * @author Nicolas Gramlich
 * @since 09:55:19 - 11.11.2010
 */
public class Vertex implements IPoint<Float>, Comparable<Vertex> {
	// ===========================================================
	// Constants
	// ===========================================================

	public static final Vertex ZERO = new Vertex(0, 0, 0);

	// ===========================================================
	// Fields
	// ===========================================================

	float mX;
	float mY;
	float mZ;

	public Graph mGraph = null;
	private final int mHashCode;
	private final ArrayList<Edge> mConnectedEdges = new ArrayList<Edge>();
	private String mName;

	// ===========================================================
	// Constructors
	// ===========================================================

	public Vertex(final String pName, final float pX, final float pY, final float pZ) {
		this(pX, pY, pZ);
		this.mName = pName;
	}

	public Vertex(final float pX, final float pY, final float pZ) {
		this.mX = pX;
		this.mY = pY;
		this.mZ = pZ;

		final int prime = 31;
		int result = 1;
		result = prime * result + Float.floatToIntBits(this.mX);
		result = prime * result + Float.floatToIntBits(this.mY);
		result = prime * result + Float.floatToIntBits(this.mZ);
		this.mHashCode = result;
	}

	// ===========================================================
	// Getter & Setter
	// ===========================================================

	public float dot(final Vertex pOtherVertex) {
		return this.mX * pOtherVertex.mX + this.mY * pOtherVertex.mY + this.mZ * pOtherVertex.mZ;
	}

	public float getDistanceTo(final Vertex pVertex) {
		final float dX = this.mX - pVertex.mX;
		final float dY = this.mY - pVertex.mY;
		final float dZ = this.mZ - pVertex.mZ;

		return (float)Math.sqrt(dX * dX + dY * dY + dZ * dZ);
	}

	public Graph getGraph() {
		return this.mGraph;
	}

	public void setGraph(final Graph pGraph) {
		this.mGraph = pGraph;
	}

	public void removeConnectedEdge(final Edge pEdge) throws IllegalStateException {
		if(this.mConnectedEdges.size() == 0) {
			System.out.println("Tried to remove from" + this.toString() + " : " + pEdge + "  but was empty.");
			return;
		}
		this.mConnectedEdges.remove(pEdge);
		//		System.out.println("Removing from " + this.toString() + " : " + pEdge + "  Remaining: " + this.mConnectedEdges);
	}

	public void addConnectedEdge(final Edge pEdge) {
		this.mConnectedEdges.add(pEdge);
	}

	public boolean isLeaf() {
		return this.mConnectedEdges.size() == 1; // TODO Could still be a leaf if there are more than 1 edges as the edges might be "indentical".
	}

	public ArrayList<Edge> getConnectedEdges() {
		return this.mConnectedEdges;
	}

	public void removeNonEMSTInternalEdges() {
		final ArrayList<Edge> connectedEdges = this.mConnectedEdges;
		for(int i = connectedEdges.size() - 1; i >= 0; i--) {
			final Edge connectedEdge = connectedEdges.get(i);
			if(connectedEdge.isIntraComponentEdge() && !connectedEdge.mEuclideanMinumumSpanningTreeEdge) {
				connectedEdges.remove(i);
			}
		}
	}

	// ===========================================================
	// Methods for/from SuperClass/Interfaces
	// ===========================================================

	@Override
	public int compareTo(final Vertex pOtherVertex) {
		return Math.round(Math.signum(this.getDistanceTo(Vertex.ZERO) - pOtherVertex.getDistanceTo(Vertex.ZERO)));
	}

	@Override
	public String toString() {
		if(this.mName != null) {
			return this.mName;
		} else {
			return "[" + this.mX + ", " + this.mY + ", " + this.mZ + "]";
		}
	}

	@Override
	public Float getCoordinate(final int pDimension) {
		switch(pDimension) {
			case 0:
				return this.mX;
			case 1:
				return this.mY;
			case 2:
				return this.mZ;
			default:
				throw new IllegalArgumentException();
		}
	}

	@Override
	public int getDimensions() {
		return 2;
	}

	@Override
	public int hashCode() {
		return this.mHashCode;
	}

	// ===========================================================
	// Methods
	// ===========================================================

	public Vertex sub(final Vertex pVertex) {
		return new Vertex(this.mX - pVertex.mX, this.mY - pVertex.mY, this.mZ - pVertex.mZ);
	}

	// ===========================================================
	// Inner and Anonymous Classes
	// ===========================================================
}
